#!/usr/bin/env python
"""
Acorn-SDR (A Collaboration of Radio Nodes)
(c) Bob Cowdery (G3UKB) 2009

File: dttsp_tx_service.py
Date: July 2009

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

The author can be reached by email at

bob@g3ukb.co.uk

================================================================================
Module Description:

The dttsp_tx implementation.

"""

#-------------------------------------------------------------------------------
# System imports
import sys
import os
from Numeric import *
from time import sleep
from scipy import signal
from scipy.signal.signaltools import resample
import Ice, IceStorm

# Application imports
import libdttsp
import ice_helpers
import utils
from dttsp_map import *

#===============================================================================
# The DttSp interface class
class Dsp:
    
    #---------------------------------------------------------------------------   
    def __init__(self, persist, update_publisher, display_publisher, frames, rate):
        self.persist = persist
        self.update_pub = update_publisher
        self.display_pub = display_publisher
        self.frames = frames
        self.rate = rate
        self.current_filter = '2K1'
        self.current_mode = 'LSB'
        self.current_osc = 0.0
        self.commands = []
        self.updates = {}
       
        # These need to follow rate and frame changes
        self.max_offset = float(rate)/1000000.0
        # We want to send display updates every 100ms approx
        self.update_counter = int((float(self.rate) * 0.1)/float(self.frames))
        self.cycle = 0
      
    #---------------------------------------------------------------------------  
    def start(self):
        libdttsp.pySetup()
        # TODO: Need to set up for TX here
     
    #---------------------------------------------------------------------------   
    def stop(self):
        libdttsp.pyCloseup()
      
    #---------------------------------------------------------------------------
    def process(self, l, r):
        # Do dsp
        real_out = libdttsp.pyDoSamples(l, r, self.frames)
        real_out_as_float = asarray(real_out, 'f')
        # Update any commands
        for item in self.commands:
            #print 'Sending ', item
            libdttsp.pyDoUpdate(item)
            self.commands = []
            
        # See if its time for a display update
        if self.cycle == self.update_counter:
            spec = libdttsp.pyDoPanadapter(0)
            meter = libdttsp.pyDoRxMeter(0)
            #print spec
            spec_out_as_float = asarray(spec, 'f')
            self.display_pub.panData(spec_out_as_float.tolist())
            meter_out_as_float = asarray(meter, 'f')
            self.display_pub.meterData(meter_out_as_float.tolist())
            self.cycle = 0
        else:
            self.cycle += 1
        
        processed_smpls = real_out_as_float * self.current_af_gain
        
    #---------------------------------------------------------------------------
    def mode(self, new_mode):
        self.current_mode = new_mode
        self.commands.append('setMode ' + str(mode_map[new_mode]))
        self.filter(self.current_filter)
        self.update_pub.modeUpdate(new_mode)
        
    #---------------------------------------------------------------------------
    def filter(self, new_filter):
        self.current_filter = new_filter
        if self.current_mode == 'LSB' or self.current_mode == 'CW-L' or self.current_mode == 'DIGI-L':
            low = - filter_map[new_filter][1]
            high = - filter_map[new_filter][0]
        elif self.current_mode == 'AM' or self.current_mode == 'SAM' or self.current_mode == 'FM-N':
            low = -filter_map[new_filter][1]
            high = filter_map[new_filter][1]
        else:
            low = filter_map[new_filter][0]
            high = filter_map[new_filter][1]
        self.commands.append('setFilter ' + str(low) + ' ' + str(high))
        self.update_pub.filterUpdate(new_filter)
    
    #---------------------------------------------------------------------------
    def osc_set(self, offset):
        # The oscillator can go +- rate
        self.current_osc =  offset
        self.commands.append('setOsc ' + str(self.current_osc*1000000))
        self.update_pub.oscUpdate(self.current_osc)
            
    #---------------------------------------------------------------------------
    def osc_inc(self, inc):
        # The oscillator can go +- rate
        new_osc = self.current_osc + inc
        if (new_osc <= self.max_offset) and (new_osc >= -self.max_offset):
            # Within limits
            self.current_osc = self.current_osc + inc
            self.commands.append('setOsc ' + str(self.current_osc*1000000))
            self.update_pub.oscUpdate(self.current_osc)
    
    #-----------------------------------------------------------------------
    # Explicitly called config handlers
    # We need to handle these carefully else the audio will get messed up.
    #-----------------------------------------------------------------------
        
    #-----------------------------------------------------------------------
    def apply_updates(self):
        # This must be called at the end of a cycle so we don't apply updates
        # when the DSP is running
        # Let this run straight away as we will match any stray buffers in the pipeline
        # by sending an empty response of the correct size
        # ToDo: We should have a mutex around here as there is a window
        for key in self.updates.keys():
            self.updates[key][1](self.updates[key][0])
            if self.updates[key][2]:
                # Reinitialise
                self.max_offset = float(self.rate)/1000000.0
                self.update_counter = int((float(self.rate) * 0.1)/float(self.frames))
                self.cycle = 0
        self.updates.clear()
